package com.jwds.digital.bios.business.service.service.belt.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jwds.digital.bios.business.core.constants.BeltCloudConstant;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.BeltInfoInputParamDTO;
import com.jwds.digital.bios.business.core.infrastructure.dto.feign.ElectricMachineHistoryReqDTO;
import com.jwds.digital.bios.business.core.infrastructure.dto.feign.FlowBasicReqDTO;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.BasicPowerType;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.BeltPerformanceDay;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.BeltPerformanceHour;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.BeltPerformanceMinute;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.BeltPerformanceMonth;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.BeltPerformanceWeek;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.BeltTrack;
import com.jwds.digital.bios.business.core.infrastructure.telegram.BeltRouteBelongData;
import com.jwds.digital.bios.business.core.infrastructure.vo.feign.BasicDeviceRespVO;
import com.jwds.digital.bios.business.core.infrastructure.vo.feign.FlowBasicRespVO;
import com.jwds.digital.bios.business.core.infrastructure.vo.feign.FlowCurrentJobRespVO;
import com.jwds.digital.bios.business.core.utils.BeltCloudTimeUtil;
import com.jwds.digital.bios.business.core.utils.BeltCloudUtil;
import com.jwds.digital.bios.business.rpc.feign.BasicDeviceFeign;
import com.jwds.digital.bios.business.rpc.feign.FlowBasicFeign;
import com.jwds.digital.bios.business.rpc.feign.FlowCurrentJobFeign;
import com.jwds.digital.bios.business.rpc.feign.RealTimeDataFeign;
import com.jwds.digital.bios.business.service.service.belt.IBasicPowerTypeService;
import com.jwds.digital.bios.business.service.service.belt.IBeltCoreHandleService;
import com.jwds.digital.bios.business.service.service.belt.IBeltPerformanceDayService;
import com.jwds.digital.bios.business.service.service.belt.IBeltPerformanceHourService;
import com.jwds.digital.bios.business.service.service.belt.IBeltPerformanceMinuteService;
import com.jwds.digital.bios.business.service.service.belt.IBeltPerformanceMonthService;
import com.jwds.digital.bios.business.service.service.belt.IBeltPerformanceWeekService;
import com.jwds.digital.bios.business.service.service.belt.IBeltTrackService;
import com.jwds.jmirror.data.service.api.InstanceDynamicAttrHistoryRespDTO;
import com.jwds.jmirror.data.service.api.InstanceDynamicAttrHistoryRespDTO.TimeSeries;
import com.jwds.tdc.framework.cache.service.CacheService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * <p>BeltCoreHandleServiceImpl 皮带云核心处理类</p>
 *
 * @author 林柏云
 * @Date：2024/11/13 9:24
 */

@Slf4j
@Service
public class BeltCoreHandleServiceImpl implements IBeltCoreHandleService {


  @Resource
  private CacheService cacheService;

  @Resource
  private IBeltPerformanceMinuteService beltPerformanceMinuteService;

  @Resource
  private IBeltPerformanceHourService beltPerformanceHourService;

  @Resource
  private IBeltPerformanceDayService beltPerformanceDayService;

  @Resource
  private IBeltPerformanceWeekService beltPerformanceWeekService;

  @Resource
  private IBeltPerformanceMonthService beltPerformanceMonthService;

  @Resource
  private IBasicPowerTypeService basicPowerTypeService;

  @Resource
  private IBeltTrackService beltTrackService;

  @Resource
  private FlowBasicFeign flowBasicFeign;

  @Resource
  private BasicDeviceFeign basicDeviceFeign;

  @Resource
  private RealTimeDataFeign realTimeDataFeign;

  @Resource
  private FlowCurrentJobFeign flowCurrentJobFeign;

  @Autowired
  @Qualifier("oneMinBeltTaskThreadPool")
  private ThreadPoolTaskExecutor taskExecutor;

  @PostConstruct
  public void test(){
    System.out.println("test");
  }

  /**
   * 维护正在运行的皮带
   */
  @Override
  public void defendRunningRoutes() {
    //获取当前作业信息
    List<FlowCurrentJobRespVO> currentJobList = flowCurrentJobFeign.currentExeList().getData();

    for (FlowCurrentJobRespVO job : currentJobList) {
      //维护flowNo,taskId的关系
      String flowNoKey = StrUtil.format("{}_{}",BeltCloudConstant.BC_RUNNING_FLOW_NO_KEY, job.getFlowNo());
      cacheService.setStringExpired(flowNoKey, job.getId().toString(), BeltCloudConstant.BC_RUNNING_FLOW_NO_TIME);
      String key = StrUtil.format("{}_{}_{}",BeltCloudConstant.BC_RUNNING_ROUTE_KEY, job.getFlowNo(), job.getId());
      if(!cacheService.isExists(key) && ObjectUtil.isNull(job.getFlowEndTime())){
        //不存在，并且没有结束时间，表示是正在运行的路线
        BeltRouteBelongData belongData = new BeltRouteBelongData();
        belongData.setFlowNo(job.getFlowNo());
        belongData.setTaskId(job.getId());
        belongData.setStartTime(job.getFlowStartTime());
        cacheService.setString(key, JSONUtil.toJsonStr(belongData));
      }
      if(!cacheService.isExists(key) && ObjectUtil.isNotNull(job.getFlowEndTime())){
        //不存在，并且有结束时间，表示是近期结束的路线
        BeltRouteBelongData belongData = new BeltRouteBelongData();
        belongData.setFlowNo(job.getFlowNo());
        belongData.setTaskId(job.getId());
        belongData.setStartTime(job.getFlowStartTime());
        belongData.setEndTime(job.getFlowEndTime());
        //保存5分钟，防止最后1分钟的数据没有统计到
        cacheService.setStringExpired(key, JSONUtil.toJsonStr(belongData), BeltCloudConstant.BC_RUNNING_ROUTE_KEEP_TIME);
      }
      if(cacheService.isExists(key) && ObjectUtil.isNull(job.getFlowEndTime())){
        //存在，并且没有结束时间
        continue;
      }
      if(cacheService.isExists(key) && ObjectUtil.isNotNull(job.getFlowEndTime())){
        //存在，并且有结束时间
        BeltRouteBelongData belongData = JSONUtil.toBean(cacheService.getString(key), BeltRouteBelongData.class);
        belongData.setEndTime(job.getFlowEndTime());
        //保存5分钟，防止最后1分钟的数据没有统计到
        cacheService.setStringExpired(key, JSONUtil.toJsonStr(belongData), BeltCloudConstant.BC_RUNNING_ROUTE_KEEP_TIME);
      }
    }

    //获取L1占有了的路线
    List<FlowBasicRespVO> blockedFlowList = null;
    try {
      blockedFlowList = flowBasicFeign.all(null).getData();
    } catch (Exception e) {
      log.error("获取L1占有的路线失败",e);
    }
    List<Short> blockedFlowNos = new ArrayList<>();
    if(ObjectUtil.isNotNull(blockedFlowList)){
      blockedFlowNos = blockedFlowList.stream().map(FlowBasicRespVO::getFlowNo).collect(Collectors.toList());
    }

    //获取L1占用但是不走L2系统的路线
    for (Short flowNo : blockedFlowNos) {
      //新增一个作业实绩ID
      Long taskId = BeltCloudUtil.getSnowflakeId();
      String flowNoKey = StrUtil.format("{}_{}",BeltCloudConstant.BC_RUNNING_FLOW_NO_KEY, flowNo);
      if(cacheService.isExists(flowNoKey)){
        taskId = Long.valueOf(cacheService.getString(flowNoKey));
      }
      cacheService.setStringExpired(flowNoKey, taskId.toString(), BeltCloudConstant.BC_RUNNING_FLOW_NO_TIME);
      String key = StrUtil.format("{}_{}_{}", BeltCloudConstant.BC_RUNNING_ROUTE_KEY, flowNo, taskId);
      //获取皮带轨迹数据
      List<BeltTrack> beltTrackList = beltTrackService.list(new QueryWrapper<BeltTrack>()
          .eq("flow_no", flowNo).eq("task_id", taskId).orderByAsc("record_time")
      );
      //构建正在运行的路线数据，并维护到redis
      BeltRouteBelongData belongData = null;
      if(cacheService.isExists(key)){
        belongData = JSONUtil.toBean(cacheService.getString(key), BeltRouteBelongData.class);
        if(ObjectUtil.isNull(belongData.getStartTime())){
          belongData.setStartTime(getRouteTaskStartTimeByBeltTrack(beltTrackList));
        }
        if(ObjectUtil.isNull(belongData.getEndTime())){
          belongData.setEndTime(getRouteTaskEndTimeByBeltTrack(beltTrackList, flowNo, taskId));
        }
      }else {
        belongData = new BeltRouteBelongData();
        belongData.setFlowNo(flowNo);
        belongData.setTaskId(taskId);
        belongData.setStartTime(getRouteTaskStartTimeByBeltTrack(beltTrackList));
        belongData.setEndTime(getRouteTaskEndTimeByBeltTrack(beltTrackList, flowNo, taskId));
      }
      if(ObjectUtil.isNotNull(belongData.getStartTime()) && ObjectUtil.isNotNull(belongData.getEndTime())){
        //保存5分钟，防止最后1分钟的数据没有统计到
        cacheService.setStringExpired(key, JSONUtil.toJsonStr(belongData), BeltCloudConstant.BC_RUNNING_ROUTE_KEEP_TIME);
      }else if(ObjectUtil.isNotNull(belongData.getStartTime())){
        cacheService.setString(key, JSONUtil.toJsonStr(belongData));
      }
    }
  }

  /**
   * 一分钟皮带统计任务
   */
  @Override
  public void doBeltMinuteAutoCollect() {
    Set<String> keyList = cacheService.getKeys(BeltCloudConstant.BC_RUNNING_ROUTE_KEY + "*");
    for (String key : keyList) {
      BeltRouteBelongData data = JSONUtil.toBean(cacheService.getString(key), BeltRouteBelongData.class);
      if(ObjectUtil.isNotNull(data)){
        LocalDateTime taskStartTime = data.getStartTime();
        LocalDateTime taskEndTime = data.getEndTime();
        BeltTrack lastBeltTrack = beltTrackService.getOne(new QueryWrapper<BeltTrack>().eq("flow_no", data.getFlowNo()).orderByDesc("record_time").last("limit 1"));
        if(!lastBeltTrack.getTaskId().equals(data.getTaskId()) && taskStartTime.plusMinutes(5).isBefore(LocalDateTime.now())){
          //若是脏缓存，则删除缓存数据
          cacheService.delete(key);
        }else{
          //进入统计阶段
          LocalDateTime lastStatisticsStartTime = null;
          LocalDateTime lastStatisticsEndTime = null;
          if(ObjectUtil.isNull(data.getLastStatisticsStartTime())){
            lastStatisticsStartTime = data.getStartTime();
            lastStatisticsEndTime = BeltCloudTimeUtil.getNextStatisticsEndTime(lastStatisticsStartTime, 1);
          }else{
            lastStatisticsStartTime = data.getLastStatisticsEndTime();
            lastStatisticsEndTime = BeltCloudTimeUtil.getNextStatisticsEndTime(lastStatisticsStartTime, 1);
          }
          // 若是已经结束的路线，则只统计到结束时间为止
          if(ObjectUtil.isNotNull(data.getEndTime())){
            if(taskEndTime.isBefore(lastStatisticsStartTime) || taskEndTime.isEqual(lastStatisticsStartTime)){
              continue;
            }
            if(taskEndTime.isBefore(lastStatisticsEndTime)){
              lastStatisticsEndTime = taskEndTime;
            }
          }
          data.setLastStatisticsStartTime(lastStatisticsStartTime);
          data.setLastStatisticsEndTime(lastStatisticsEndTime);
          //获取路线的基本信息
          FlowBasicReqDTO flowBasicReqDTO = new FlowBasicReqDTO();
          flowBasicReqDTO.setFlowNo(data.getFlowNo());
          FlowBasicRespVO flowBasicRespVO = flowBasicFeign.all(flowBasicReqDTO).getData().get(0);
          //获取路线下所有皮带的基本信息
          List<String> beltCodes = Arrays.asList(flowBasicRespVO.getFlowCode().split("_"));
          List<BasicDeviceRespVO> basicDeviceRespVOS = basicDeviceFeign.list(null).getData();
          List<BasicDeviceRespVO> beltList = new ArrayList<>();
          for(BasicDeviceRespVO basicDeviceRespVO : basicDeviceRespVOS){
            if(beltCodes.contains(basicDeviceRespVO.getDeviceCode())){
              beltList.add(basicDeviceRespVO);
            }
          }
          // 构建 beltDTOList 皮带信息列表，并计算1分钟的能耗
          List<BeltInfoInputParamDTO> beltDTOList = buildBeltInfoInputParamDTOByBasicDeviceRespVO(beltList,data);
          addOneMinCalculateTaskToThreadPoolExecutor(beltDTOList, lastStatisticsStartTime, lastStatisticsEndTime);
          // 若路线任务已经滞后很多，则递归把历史的分钟统计任务加入线程池执行器
          data = recursionAddOneMinCalculateTaskToThreadPoolExecutor(beltDTOList, lastStatisticsStartTime, lastStatisticsEndTime, key, data);
          if(ObjectUtil.isNull(data.getEndTime())) {
            cacheService.setString(key, JSONUtil.toJsonStr(data));
          }else{
            cacheService.setStringExpired(key, JSONUtil.toJsonStr(data), BeltCloudConstant.BC_RUNNING_ROUTE_KEEP_TIME);
          }
        }
      }
    }
  }

  /**
   * 一小时皮带统计任务
   */
  @Override
  public void doBeltHourAutoCollect() {
    if(StrUtil.isEmpty(cacheService.getString(BeltCloudConstant.BC_BELT_HOUR_COLLECT_TIME_KEY))){
      LocalDateTime lastCollectTime = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
      cacheService.setString(BeltCloudConstant.BC_BELT_HOUR_COLLECT_TIME_KEY, lastCollectTime.toString());
    }else{
      LocalDateTime lastCollectTime = LocalDateTime.parse(cacheService.getString(BeltCloudConstant.BC_BELT_HOUR_COLLECT_TIME_KEY));
      if(lastCollectTime.plusHours(1).isBefore(LocalDateTime.now())) {
        doOneHourCalculateByOneMinData(lastCollectTime, lastCollectTime.plusHours(1));
        cacheService.setString(BeltCloudConstant.BC_BELT_HOUR_COLLECT_TIME_KEY, lastCollectTime.plusHours(1).toString());
      }
    }
  }

  /**
   * 一天皮带统计任务
   */
  @Override
  public void doBeltDayAutoCollect() {
    if(StrUtil.isEmpty(cacheService.getString(BeltCloudConstant.BC_BELT_DAY_COLLECT_TIME_KEY))){
      LocalDateTime lastCollectTime = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
      cacheService.setString(BeltCloudConstant.BC_BELT_DAY_COLLECT_TIME_KEY, lastCollectTime.toString());
    }else{
      LocalDateTime lastCollectTime = LocalDateTime.parse(cacheService.getString(BeltCloudConstant.BC_BELT_DAY_COLLECT_TIME_KEY));
      if(lastCollectTime.plusDays(1).isBefore(LocalDateTime.now())) {
        doOneDayCalculateByOneHourData(lastCollectTime, lastCollectTime.plusDays(1));
        cacheService.setString(BeltCloudConstant.BC_BELT_DAY_COLLECT_TIME_KEY, lastCollectTime.plusDays(1).toString());
      }
    }
  }

  /**
   * 一周皮带统计任务
   */
  @Override
  public void doBeltWeekAutoCollect() {
    if(StrUtil.isEmpty(cacheService.getString(BeltCloudConstant.BC_BELT_WEEK_COLLECT_TIME_KEY))){
      LocalDateTime lastCollectTime = LocalDateTime.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).withHour(0).withMinute(0).withSecond(0).withNano(0);
      cacheService.setString(BeltCloudConstant.BC_BELT_WEEK_COLLECT_TIME_KEY, lastCollectTime.toString());
    }else{
      LocalDateTime lastCollectTime = LocalDateTime.parse(cacheService.getString(BeltCloudConstant.BC_BELT_WEEK_COLLECT_TIME_KEY));
      if(lastCollectTime.plusWeeks(1).isBefore(LocalDateTime.now())) {
        doOneWeekCalculateByOneDayData(lastCollectTime, lastCollectTime.plusWeeks(1));
        cacheService.setString(BeltCloudConstant.BC_BELT_WEEK_COLLECT_TIME_KEY, lastCollectTime.plusWeeks(1).toString());
      }
    }
  }

  /**
   * 一个月皮带统计任务
   */
  @Override
  public void doBeltMonthAutoCollect() {
    if(StrUtil.isEmpty(cacheService.getString(BeltCloudConstant.BC_BELT_MONTH_COLLECT_TIME_KEY))){
      LocalDateTime lastCollectTime = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
      cacheService.setString(BeltCloudConstant.BC_BELT_MONTH_COLLECT_TIME_KEY, lastCollectTime.toString());
    }else{
      LocalDateTime lastCollectTime = LocalDateTime.parse(cacheService.getString(BeltCloudConstant.BC_BELT_MONTH_COLLECT_TIME_KEY));
      if(lastCollectTime.plusMonths(1).isBefore(LocalDateTime.now())) {
        doOneMonthCalculateByOneDayData(lastCollectTime, lastCollectTime.plusMonths(1));
        cacheService.setString(BeltCloudConstant.BC_BELT_MONTH_COLLECT_TIME_KEY, lastCollectTime.plusMonths(1).toString());
      }
    }
  }

  /**
   * 递归把历史的分钟统计任务加入线程池执行器
   * @param beltDTOList
   * @param lastStatisticsStartTime
   * @param lastStatisticsEndTime
   * @param key
   * @param data
   * @return
   */
  private BeltRouteBelongData recursionAddOneMinCalculateTaskToThreadPoolExecutor(List<BeltInfoInputParamDTO> beltDTOList, LocalDateTime lastStatisticsStartTime, LocalDateTime lastStatisticsEndTime, String key, BeltRouteBelongData data){
    //若路线任务的统计时间已经落后于当前时间超过1分钟，并且路线任务没有结束或者路线任务统计时间落后任务结束时间超过1分钟
    if(lastStatisticsEndTime.plusMinutes(1).isBefore(LocalDateTime.now())
        && (ObjectUtil.isNull(data.getEndTime()) || lastStatisticsEndTime.isBefore(data.getEndTime()))){
      lastStatisticsStartTime = lastStatisticsEndTime;
      lastStatisticsEndTime = BeltCloudTimeUtil.getNextStatisticsEndTime(lastStatisticsStartTime, 1);
      if(ObjectUtil.isNotNull(data.getEndTime()) && lastStatisticsEndTime.isAfter(data.getEndTime())){
        lastStatisticsEndTime = data.getEndTime();
      }
      addOneMinCalculateTaskToThreadPoolExecutor(beltDTOList, lastStatisticsStartTime, lastStatisticsEndTime);
      data.setLastStatisticsStartTime(lastStatisticsStartTime);
      data.setLastStatisticsEndTime(lastStatisticsEndTime);
      return recursionAddOneMinCalculateTaskToThreadPoolExecutor(beltDTOList, lastStatisticsStartTime, lastStatisticsEndTime, key, data);
    }else {
      return data;
    }
  }

  /**
   * 以一条路线任务为一个单位输入，按单条皮带进行计算，添加到线程池执行器，一分钟能耗的计算
   * @Param beltDTOList  皮带信息列表
   * @Param lastStatisticsStartTime  上一次统计开始时间
   * @Param lastStatisticsEndTime  上一次统计结束时间
   */
  private void addOneMinCalculateTaskToThreadPoolExecutor(List<BeltInfoInputParamDTO> beltDTOList, LocalDateTime lastStatisticsStartTime, LocalDateTime lastStatisticsEndTime){

    //查询线路作业下所有皮带一分钟内的电流、电压、功率、功率因素、能耗数据
    List<String> beltCodeList = beltDTOList.stream().map(BeltInfoInputParamDTO::getBeltCode).collect(Collectors.toList());
    ElectricMachineHistoryReqDTO reqDTO = new ElectricMachineHistoryReqDTO();
    reqDTO.setStartTime(lastStatisticsStartTime);
    reqDTO.setEndTime(lastStatisticsEndTime);
    reqDTO.setInstanceEngNameList(beltCodeList);
    Map<String, List<InstanceDynamicAttrHistoryRespDTO>> beltCollectListMap = realTimeDataFeign.queryElectricMachineHistory(reqDTO).getData();

    for(BeltInfoInputParamDTO beltDTO : beltDTOList){
      taskExecutor.execute(() -> {
        //首先判断一分钟内是否有数据，若无则不计算
        BeltPerformanceMinute selectMinuteData = beltPerformanceMinuteService.getOne(new QueryWrapper<BeltPerformanceMinute>().lambda()
            .eq(BeltPerformanceMinute::getBeltCode, beltDTO.getBeltCode())
            .eq(BeltPerformanceMinute::getFlowNo, beltDTO.getFlowNo())
            .eq(BeltPerformanceMinute::getTaskId, beltDTO.getTaskId())
            .eq(BeltPerformanceMinute::getStartTime, lastStatisticsStartTime)
            .eq(BeltPerformanceMinute::getEndTime, lastStatisticsEndTime)
            .last("LIMIT 1")
        );
        if(ObjectUtil.isNotNull(selectMinuteData)){
          log.warn("皮带{}路线{}任务{}开始时间{}结束时间{}的一分钟能耗数据已存在，无需重复计算", beltDTO.getBeltCode(), beltDTO.getFlowNo(), beltDTO.getTaskId(), lastStatisticsStartTime, lastStatisticsEndTime);
          return;
        }
        // 一分钟内的电流
        List<BigDecimal> electricCurrentList = new ArrayList<>();
        // 一分钟内的电压
        List<BigDecimal> voltageList = new ArrayList<>();
        // 一分钟内的功率
        List<BigDecimal> powerList = new ArrayList<>();
        // 一分钟功率因素
        List<BigDecimal> powerFactorList = new ArrayList<>();
        // 一分钟内能耗
        List<BigDecimal> kwhList = new ArrayList<>();
        // 该皮带一分钟内的电流、电压、功率、功率因素、能耗数据
        List<InstanceDynamicAttrHistoryRespDTO> beltCollectList = beltCollectListMap.get(beltDTO.getBeltCode());
        for(InstanceDynamicAttrHistoryRespDTO instanceHisDTO : beltCollectList){
          List<TimeSeries> dataList = instanceHisDTO.getData();
          if(!CollectionUtils.isEmpty(dataList)) {
            List<BigDecimal> resultList = dataList.stream().map(TimeSeries::getValue).map(BigDecimal::new).collect(Collectors.toList());
            if (instanceHisDTO.getEngName().equals(BeltCloudUtil.ELECTRIC_ENG_NAME)) {
              electricCurrentList = resultList;
            } else if (instanceHisDTO.getEngName().equals(BeltCloudUtil.VOLTAGE_ENG_NAME)) {
              voltageList = resultList;
            } else if (instanceHisDTO.getEngName().equals(BeltCloudUtil.POWER_ENG_NAME)) {
              powerList = resultList;
            } else if (instanceHisDTO.getEngName().equals(BeltCloudUtil.POWER_FACTOR_ENG_NAME)) {
              powerFactorList = resultList;
            } else if (instanceHisDTO.getEngName().equals(BeltCloudUtil.KWH_FACTOR_ENG_NAME)) {
              kwhList = resultList;
            }
          }
        }

        if(CollectionUtils.isEmpty(electricCurrentList)){
          log.warn("任务{}电流采集数据为空，无法计算电耗", beltDTO.getTaskId());
          return;
        }
        if(CollectionUtils.isEmpty(voltageList)){
          log.warn("任务{}电压采集数据为空，无法计算电耗", beltDTO.getTaskId());
          return;
        }
        if(CollectionUtils.isEmpty(powerList)){
          log.warn("任务{}功率采集数据为空，无法计算电耗", beltDTO.getTaskId());
          return;
        }
        if(CollectionUtils.isEmpty(powerFactorList)){
          log.warn("任务{}功率因素采集数据为空，无法计算电耗", beltDTO.getTaskId());
          return;
        }
        if(CollectionUtils.isEmpty(kwhList)){
          log.warn("任务{}能耗采集数据为空，无法计算电耗", beltDTO.getTaskId());
          return;
        }

        // 一分钟内的平均电流
        double oneMinElectricCurrent = electricCurrentList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0);
        // 一分钟内的平均电压
        double oneMinVoltage = voltageList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0);
        // 一分钟内的平均功率
        double oneMinPower = powerList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0);
        // 一分钟内的平均功率因素
        double oneMinPowerFactor = powerFactorList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0);
        if(oneMinElectricCurrent < 0.01 || oneMinVoltage < 0.01 || oneMinPower < 0.01 || oneMinPowerFactor < 0.01){
          log.warn("任务{}一分钟内的平均电流{}、电压{}、功率{}、功率因素{}存在0，无法计算电耗"
              , beltDTO.getTaskId(), oneMinElectricCurrent, oneMinVoltage, oneMinPower, oneMinPowerFactor);
          return;
        }
        // 求输入功率
        BigDecimal inputPower = BigDecimal.valueOf(Math.sqrt(3)*oneMinElectricCurrent*oneMinVoltage*oneMinPowerFactor);
        // 求电机负载率
        BigDecimal motorLoadRate = BigDecimal.valueOf(1.013).multiply(inputPower.divide(beltDTO.getMotorRatedPower()))
            .subtract(BigDecimal.ONE.divide(beltDTO.getMotorLossFactor().add(BigDecimal.ONE))
                .multiply(BigDecimal.ONE.divide(beltDTO.getMotorRatedEfficiency()).subtract(BigDecimal.ONE))
                .multiply(BigDecimal.valueOf(0.85)
                    .multiply(inputPower.divide(beltDTO.getMotorRatedPower()))
                    .multiply(inputPower.divide(beltDTO.getMotorRatedPower()))
                    .add(beltDTO.getMotorLossFactor())
                )
            );
        // 求输出功率
        BigDecimal outputPower = BigDecimal.valueOf(Math.sqrt(3)*oneMinElectricCurrent*oneMinVoltage*oneMinPowerFactor);
        // 求电机运行效率
        BigDecimal motorRunEfficiency = outputPower.divide(inputPower, 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
        // 求阻力
        BigDecimal avgResistance = outputPower.multiply(beltDTO.getMotorImbalanceNum())
            .multiply(BigDecimal.valueOf(1000))
            .divide(beltDTO.getBeltSpeed())
            .setScale(0, RoundingMode.HALF_UP);
        // 求电耗
        BigDecimal consumeElectricity = kwhList.get(kwhList.size() - 1).subtract(kwhList.get(0));
        // 构建分钟级别数据
        BeltPerformanceMinute minuteData = new BeltPerformanceMinute();
        minuteData.setFlowNo(beltDTO.getFlowNo());
        minuteData.setBeltCode(beltDTO.getBeltCode());
        minuteData.setTaskId(beltDTO.getTaskId());
        minuteData.setStartTime(lastStatisticsStartTime);
        minuteData.setEndTime(lastStatisticsEndTime);
        minuteData.setAvgLoadRate(motorLoadRate);
        minuteData.setAvgPower(BigDecimal.valueOf(oneMinPower).setScale(0, RoundingMode.HALF_UP));
        minuteData.setPowerConsumption(consumeElectricity);
        minuteData.setRuntime(Integer.parseInt(ChronoUnit.SECONDS.between(minuteData.getStartTime(), minuteData.getEndTime()) +""));
        minuteData.setOperationEfficiency(motorRunEfficiency);
        minuteData.setPowerType(getPowerTypeByTime(minuteData.getStartTime()));
        minuteData.setAvgResistance(avgResistance);
        Map<String, Object> resultMap = getBeltTotalTransportByTimeSlotAndLoadSignal(minuteData.getFlowNo(), minuteData.getBeltCode(), minuteData.getStartTime(), minuteData.getEndTime());
        minuteData.setTotalTransport((BigDecimal)resultMap.get("totalTransport"));
        minuteData.setLoadStatus((Integer)resultMap.get("loadSignal"));
        minuteData.setBelongDate(null);
        beltPerformanceMinuteService.save(minuteData);
      });
    }
  }

  /**
   * 根据皮带效能一分钟数据计算一小时的皮带效能数据
   * @param lastStatisticsStartTime
   * @param lastStatisticsEndTime
   */
  private void doOneHourCalculateByOneMinData(LocalDateTime lastStatisticsStartTime, LocalDateTime lastStatisticsEndTime){
    //找出一小时内所有皮带的一分钟数据
    List<BeltPerformanceMinute> minuteDataList = beltPerformanceMinuteService
        .list(new LambdaQueryWrapper<BeltPerformanceMinute>()
            .ge(BeltPerformanceMinute::getStartTime, lastStatisticsStartTime)
            .le(BeltPerformanceMinute::getEndTime, lastStatisticsEndTime)
        );
    // 按照皮带id分组
    Map<String, List<BeltPerformanceMinute>> minuteDataMap = minuteDataList.stream().collect(Collectors.groupingBy(BeltPerformanceMinute::getBeltCode));
    // 遍历每个皮带，统计一小时的数据
    minuteDataMap.forEach((beltCode, minuteDataListByBelt) -> {
      // 按照作业任务分组
      Map<Long, List<BeltPerformanceMinute>> minuteAndTaskDataMap = minuteDataListByBelt.stream().collect(Collectors.groupingBy(BeltPerformanceMinute::getTaskId));
      minuteAndTaskDataMap.forEach((taskId, minuteDataListByTask) -> {
        //判断是否已经计算过一个小时的数据
        BeltPerformanceHour selectHourData = beltPerformanceHourService.getOne(new LambdaQueryWrapper<BeltPerformanceHour>()
            .eq(BeltPerformanceHour::getBeltCode, beltCode)
            .eq(BeltPerformanceHour::getTaskId, taskId)
            .eq(BeltPerformanceHour::getStartTime, lastStatisticsStartTime)
            .eq(BeltPerformanceHour::getEndTime, lastStatisticsEndTime));
        if(ObjectUtil.isNotNull(selectHourData)){
          log.warn("皮带{}路线{}任务{}开始时间{}结束时间{}的一小时能耗数据已存在，无需重复计算", beltCode, minuteDataListByTask.get(0).getFlowNo(), taskId, lastStatisticsStartTime, lastStatisticsEndTime);
          return;
        }
        List<BeltPerformanceMinute> oneMinLoadList = minuteDataListByTask.stream().filter(minuteData -> minuteData.getLoadStatus() == 1).collect(Collectors.toList());
        List<BeltPerformanceMinute> oneMinUnLoadList = minuteDataListByTask.stream().filter(minuteData -> minuteData.getLoadStatus() == 0).collect(Collectors.toList());
        // 负载时长、空载时长
        int oneHourLoadRunningTime = oneMinLoadList.stream().mapToInt(BeltPerformanceMinute::getRuntime).sum();
        int oneHourUnLoadRunningTime = oneMinUnLoadList.stream().mapToInt(BeltPerformanceMinute::getRuntime).sum();
        // 负载率、负载负载率、空负载率
        BigDecimal oneHourAvgAllLoadRate = minuteDataListByTask.stream().map(BeltPerformanceMinute::getAvgLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(minuteDataListByTask.size()), 0, RoundingMode.HALF_UP);
        BigDecimal oneHourAvgLoadRate = oneMinLoadList.stream().map(BeltPerformanceMinute::getAvgLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(oneMinLoadList.size()), 0, RoundingMode.HALF_UP);
        BigDecimal oneHourAvgUnLoadRate = oneMinUnLoadList.stream().map(BeltPerformanceMinute::getAvgLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(oneMinUnLoadList.size()), 0, RoundingMode.HALF_UP);
        // 负载占比
        BigDecimal oneHourLoadRatio = BigDecimal.valueOf(oneHourLoadRunningTime).divide(BigDecimal.valueOf(oneHourUnLoadRunningTime).add(BigDecimal.valueOf(oneHourLoadRunningTime)), 2, BigDecimal.ROUND_HALF_UP);
        // 所有电耗、负载电耗、空载电耗
        BigDecimal totalPowerConsumption = minuteDataListByTask.stream().map(BeltPerformanceMinute::getPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal oneHourLoadPowerConsumption = oneMinLoadList.stream().map(BeltPerformanceMinute::getPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal oneHourUnLoadPowerConsumption = oneMinUnLoadList.stream().map(BeltPerformanceMinute::getPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 运行效率
        BigDecimal oneHourWorkEfficiency = minuteDataListByTask.stream().map(BeltPerformanceMinute::getOperationEfficiency).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(minuteDataListByTask.size()), 2, RoundingMode.HALF_UP);
        // 运输量
        BigDecimal oneHourTransport = minuteDataListByTask.stream().map(BeltPerformanceMinute::getTotalTransport).reduce(BigDecimal.ZERO, BigDecimal::add);

        //构建并保存数据
        BeltPerformanceHour hourData = new BeltPerformanceHour();
        hourData.setStartTime(lastStatisticsStartTime);
        hourData.setEndTime(lastStatisticsEndTime);
        hourData.setBeltId(minuteDataListByTask.get(0).getBeltId());
        hourData.setBeltCode(beltCode);
        hourData.setTaskId(taskId);
        hourData.setFlowNo(minuteDataListByTask.get(0).getFlowNo());
        hourData.setRouteId(minuteDataListByTask.get(0).getRouteId());
        hourData.setAvgAllLoadRate(oneHourAvgAllLoadRate);
        hourData.setAvgLoadRate(oneHourAvgLoadRate);
        hourData.setAvgNoLoadRate(oneHourAvgUnLoadRate);
        hourData.setLoadRateRatio(oneHourLoadRatio);
        hourData.setTotalPowerConsumption(totalPowerConsumption);
        hourData.setLoadPowerConsumption(oneHourLoadPowerConsumption);
        hourData.setNoLoadPowerConsumption(oneHourUnLoadPowerConsumption);
        hourData.setOperationEfficiency(oneHourWorkEfficiency);
        hourData.setWorkRate(null);
        hourData.setAllRuntime(BigDecimal.valueOf(oneHourLoadRunningTime+oneHourUnLoadRunningTime).divide(BigDecimal.valueOf(60), 2, BigDecimal.ROUND_HALF_UP));
        hourData.setLoadRuntime(BigDecimal.valueOf(oneHourLoadRunningTime).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP));
        hourData.setNoLoadRuntime(BigDecimal.valueOf(oneHourUnLoadRunningTime).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP));
        hourData.setTotalTransport(oneHourTransport);
        // 峰谷平尖电耗
        BigDecimal spikePowerConsumption = minuteDataListByTask.stream().filter(e->e.getPowerType() == 1).map(BeltPerformanceMinute::getPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal peakPowerConsumption = minuteDataListByTask.stream().filter(e->e.getPowerType() == 2).map(BeltPerformanceMinute::getPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal flatPowerConsumption = minuteDataListByTask.stream().filter(e->e.getPowerType() == 3).map(BeltPerformanceMinute::getPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal valleyPowerConsumption = minuteDataListByTask.stream().filter(e->e.getPowerType() == 4).map(BeltPerformanceMinute::getPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
        hourData.setSpikePowerConsumption(spikePowerConsumption);
        hourData.setPeakPowerConsumption(peakPowerConsumption);
        hourData.setFlatPowerConsumption(flatPowerConsumption);
        hourData.setValleyPowerConsumption(valleyPowerConsumption);
        hourData.setWorkEfficiency(oneHourTransport.divide(hourData.getAllRuntime().divide(BigDecimal.valueOf(60)), 2, RoundingMode.HALF_UP));
        beltPerformanceHourService.save(hourData);
      });
    });
  }

  /**
   * 根据皮带效能小时数据计算日级别的皮带效能数据
   * @param lastStatisticsStartTime
   * @param lastStatisticsEndTime
   */
  private void doOneDayCalculateByOneHourData(LocalDateTime lastStatisticsStartTime, LocalDateTime lastStatisticsEndTime){
    //找出一周内所有皮带的小时数据
    List<BeltPerformanceHour> hourDataList = beltPerformanceHourService
        .list(new LambdaQueryWrapper<BeltPerformanceHour>()
            .ge(BeltPerformanceHour::getStartTime, lastStatisticsStartTime)
            .le(BeltPerformanceHour::getEndTime, lastStatisticsEndTime)
        );
    // 按照皮带id分组
    Map<String, List<BeltPerformanceHour>> hourDataMap = hourDataList.stream().collect(Collectors.groupingBy(BeltPerformanceHour::getBeltCode));
    hourDataMap.forEach((beltCode, hourDataListByBelt) -> {
      //判断是否已经计算过一天的数据
      BeltPerformanceDay selectDayData = beltPerformanceDayService.getOne(new LambdaQueryWrapper<BeltPerformanceDay>()
          .eq(BeltPerformanceDay::getBeltCode, beltCode)
          .eq(BeltPerformanceDay::getStartTime, lastStatisticsStartTime)
          .eq(BeltPerformanceDay::getEndTime, lastStatisticsEndTime));
      if(ObjectUtil.isNotNull(selectDayData)){
        log.warn("皮带{}开始时间{}结束时间{}的一天能耗数据已存在，无需重复计算", beltCode, lastStatisticsStartTime, lastStatisticsEndTime);
        return;
      }
      // 负载率、负载负载率、空负载率
      BigDecimal oneDayAvgAllLoadRate = hourDataListByBelt.stream().map(BeltPerformanceHour::getAvgAllLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(hourDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      BigDecimal oneDayAvgLoadRate = hourDataListByBelt.stream().map(BeltPerformanceHour::getAvgLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(hourDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      BigDecimal oneDayAvgNoLoadRate = hourDataListByBelt.stream().map(BeltPerformanceHour::getAvgNoLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(hourDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      // 负载时长、空载时长
      BigDecimal oneDayLoadRunningTime = hourDataListByBelt.stream().map(BeltPerformanceHour::getLoadRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneDayUnLoadRunningTime = hourDataListByBelt.stream().map(BeltPerformanceHour::getNoLoadRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneDayAllRunningTime = hourDataListByBelt.stream().map(BeltPerformanceHour::getAllRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      // 负载率占比
      BigDecimal oneDayLoadRateProportion = oneDayLoadRunningTime.divide(oneDayAllRunningTime, 2, RoundingMode.HALF_UP);
      // 所有电耗、负载电耗、空载电耗
      BigDecimal oneDayAllPowerConsumption = hourDataListByBelt.stream().map(BeltPerformanceHour::getTotalPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneDayLoadPowerConsumption = hourDataListByBelt.stream().map(BeltPerformanceHour::getLoadPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneDayUnLoadPowerConsumption = hourDataListByBelt.stream().map(BeltPerformanceHour::getNoLoadPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      // 运行效率
      BigDecimal oneDayRunningEfficiency = hourDataListByBelt.stream().map(BeltPerformanceHour::getOperationEfficiency).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(hourDataListByBelt.size()), 2, RoundingMode.HALF_UP);
      // 运输量
      BigDecimal oneDayTransport = hourDataListByBelt.stream().map(BeltPerformanceHour::getTotalTransport).reduce(BigDecimal.ZERO, BigDecimal::add);

      //构建并保存数据
      BeltPerformanceDay beltPerformanceDay = new BeltPerformanceDay();
      beltPerformanceDay.setStartTime(lastStatisticsStartTime);
      beltPerformanceDay.setEndTime(lastStatisticsEndTime);
      beltPerformanceDay.setBeltId(hourDataListByBelt.get(0).getBeltId());
      beltPerformanceDay.setBeltCode(beltCode);
      beltPerformanceDay.setAvgAllLoadRate(oneDayAvgAllLoadRate);
      beltPerformanceDay.setAvgLoadRate(oneDayAvgLoadRate);
      beltPerformanceDay.setAvgNoLoadRate(oneDayAvgNoLoadRate);
      beltPerformanceDay.setLoadRuntime(oneDayLoadRunningTime);
      beltPerformanceDay.setNoLoadRuntime(oneDayUnLoadRunningTime);
      beltPerformanceDay.setAllRuntime(oneDayAllRunningTime);
      beltPerformanceDay.setLoadRateRatio(oneDayLoadRateProportion);
      beltPerformanceDay.setTotalPowerConsumption(oneDayAllPowerConsumption);
      beltPerformanceDay.setLoadPowerConsumption(oneDayLoadPowerConsumption);
      beltPerformanceDay.setNoLoadPowerConsumption(oneDayUnLoadPowerConsumption);
      beltPerformanceDay.setOperationEfficiency(oneDayRunningEfficiency);
      beltPerformanceDay.setTotalTransport(oneDayTransport);
      // 峰谷平尖电耗
      BigDecimal peakPowerConsumption = hourDataListByBelt.stream().map(BeltPerformanceHour::getPeakPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal valleyPowerConsumption = hourDataListByBelt.stream().map(BeltPerformanceHour::getValleyPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal flatPowerConsumption = hourDataListByBelt.stream().map(BeltPerformanceHour::getFlatPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal spikePowerConsumption = hourDataListByBelt.stream().map(BeltPerformanceHour::getSpikePowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      beltPerformanceDay.setPeakPowerConsumption(peakPowerConsumption);
      beltPerformanceDay.setValleyPowerConsumption(valleyPowerConsumption);
      beltPerformanceDay.setFlatPowerConsumption(flatPowerConsumption);
      beltPerformanceDay.setSpikePowerConsumption(spikePowerConsumption);
      beltPerformanceDay.setWorkRate(null);
      beltPerformanceDay.setWorkEfficiency(oneDayTransport.divide(beltPerformanceDay.getAllRuntime().divide(BigDecimal.valueOf(60)), 2, RoundingMode.HALF_UP));
      beltPerformanceDayService.save(beltPerformanceDay);
    });
  }

  /**
   * 根据皮带效能日数据计算周级别的皮带效能数据
   * @param lastStatisticsStartTime
   * @param lastStatisticsEndTime
   */
  private void doOneWeekCalculateByOneDayData(LocalDateTime lastStatisticsStartTime, LocalDateTime lastStatisticsEndTime){
    //找出一周内所有皮带的小时数据
    List<BeltPerformanceDay> dayDataList = beltPerformanceDayService
        .list(new LambdaQueryWrapper<BeltPerformanceDay>()
            .ge(BeltPerformanceDay::getStartTime, lastStatisticsStartTime)
            .le(BeltPerformanceDay::getEndTime, lastStatisticsEndTime)
        );
    // 按照皮带id分组
    Map<String, List<BeltPerformanceDay>> dayDataMap = dayDataList.stream().collect(Collectors.groupingBy(BeltPerformanceDay::getBeltCode));
    dayDataMap.forEach((beltCode, dayDataListByBelt) -> {
      //判断是否已经计算过一周的数据
      BeltPerformanceWeek selectWeekData = beltPerformanceWeekService.getOne(new LambdaQueryWrapper<BeltPerformanceWeek>()
          .eq(BeltPerformanceWeek::getBeltCode, beltCode)
          .eq(BeltPerformanceWeek::getStartTime, lastStatisticsStartTime)
          .eq(BeltPerformanceWeek::getEndTime, lastStatisticsEndTime));
      if(ObjectUtil.isNotNull(selectWeekData)){
        log.warn("皮带{}开始时间{}结束时间{}的一周能耗数据已存在，无需重复计算", beltCode, lastStatisticsStartTime, lastStatisticsEndTime);
        return;
      }
      // 负载率、负载负载率、空负载率
      BigDecimal oneWeekAvgAllLoadRate = dayDataListByBelt.stream().map(BeltPerformanceDay::getAvgAllLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      BigDecimal oneWeekAvgLoadRate = dayDataListByBelt.stream().map(BeltPerformanceDay::getAvgLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      BigDecimal oneWeekAvgNoLoadRate = dayDataListByBelt.stream().map(BeltPerformanceDay::getAvgNoLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      // 负载时长、空载时长
      BigDecimal oneWeekLoadRunningTime = dayDataListByBelt.stream().map(BeltPerformanceDay::getLoadRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneWeekUnLoadRunningTime = dayDataListByBelt.stream().map(BeltPerformanceDay::getNoLoadRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneWeekAllRunningTime = dayDataListByBelt.stream().map(BeltPerformanceDay::getAllRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      // 负载率占比
      BigDecimal oneWeekLoadRateProportion = oneWeekLoadRunningTime.divide(oneWeekAllRunningTime, 2, RoundingMode.HALF_UP);
      // 所有电耗、负载电耗、空载电耗
      BigDecimal oneWeekAllPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getTotalPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneWeekLoadPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getLoadPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneWeekUnLoadPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getNoLoadPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      // 运行效率
      BigDecimal oneWeekRunningEfficiency = dayDataListByBelt.stream().map(BeltPerformanceDay::getOperationEfficiency).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 2, RoundingMode.HALF_UP);
      // 运输量
      BigDecimal oneWeekTransport = dayDataListByBelt.stream().map(BeltPerformanceDay::getTotalTransport).reduce(BigDecimal.ZERO, BigDecimal::add);
      //构建并保存数据
      BeltPerformanceWeek beltPerformanceWeek = new BeltPerformanceWeek();
      beltPerformanceWeek.setStartTime(lastStatisticsStartTime);
      beltPerformanceWeek.setEndTime(lastStatisticsEndTime);
      beltPerformanceWeek.setBeltId(dayDataListByBelt.get(0).getBeltId());
      beltPerformanceWeek.setBeltCode(beltCode);
      beltPerformanceWeek.setAvgAllLoadRate(oneWeekAvgAllLoadRate);
      beltPerformanceWeek.setAvgLoadRate(oneWeekAvgLoadRate);
      beltPerformanceWeek.setAvgNoLoadRate(oneWeekAvgNoLoadRate);
      beltPerformanceWeek.setLoadRuntime(oneWeekLoadRunningTime);
      beltPerformanceWeek.setNoLoadRuntime(oneWeekUnLoadRunningTime);
      beltPerformanceWeek.setAllRuntime(oneWeekAllRunningTime);
      beltPerformanceWeek.setLoadRateRatio(oneWeekLoadRateProportion);
      beltPerformanceWeek.setTotalPowerConsumption(oneWeekAllPowerConsumption);
      beltPerformanceWeek.setLoadPowerConsumption(oneWeekLoadPowerConsumption);
      beltPerformanceWeek.setNoLoadPowerConsumption(oneWeekUnLoadPowerConsumption);
      beltPerformanceWeek.setOperationEfficiency(oneWeekRunningEfficiency);
      beltPerformanceWeek.setTotalTransport(oneWeekTransport);
      // 峰谷平尖电耗
      BigDecimal peakPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getPeakPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal valleyPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getValleyPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal flatPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getFlatPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal spikePowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getSpikePowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      beltPerformanceWeek.setPeakPowerConsumption(peakPowerConsumption);
      beltPerformanceWeek.setValleyPowerConsumption(valleyPowerConsumption);
      beltPerformanceWeek.setFlatPowerConsumption(flatPowerConsumption);
      beltPerformanceWeek.setSpikePowerConsumption(spikePowerConsumption);
      beltPerformanceWeek.setWorkRate(null);
      beltPerformanceWeek.setWorkEfficiency(oneWeekTransport.divide(beltPerformanceWeek.getAllRuntime().divide(BigDecimal.valueOf(60)), 2, RoundingMode.HALF_UP));
      beltPerformanceWeekService.save(beltPerformanceWeek);
    });
  }

  /**
   * 根据皮带效能日数据计算月级别的皮带效能数据
   * @param lastStatisticsStartTime
   * @param lastStatisticsEndTime
   */
  private void doOneMonthCalculateByOneDayData(LocalDateTime lastStatisticsStartTime, LocalDateTime lastStatisticsEndTime){
    //找出一周内所有皮带的小时数据
    List<BeltPerformanceDay> dayDataList = beltPerformanceDayService
        .list(new LambdaQueryWrapper<BeltPerformanceDay>()
            .ge(BeltPerformanceDay::getStartTime, lastStatisticsStartTime)
            .le(BeltPerformanceDay::getEndTime, lastStatisticsEndTime)
        );
    // 按照皮带id分组
    Map<String, List<BeltPerformanceDay>> dayDataMap = dayDataList.stream().collect(Collectors.groupingBy(BeltPerformanceDay::getBeltCode));
    dayDataMap.forEach((beltCode, dayDataListByBelt) -> {
      //判断是否已经计算过一周的数据
      BeltPerformanceMonth selectMonthData = beltPerformanceMonthService.getOne(new LambdaQueryWrapper<BeltPerformanceMonth>()
          .eq(BeltPerformanceMonth::getBeltCode, beltCode)
          .eq(BeltPerformanceMonth::getStartTime, lastStatisticsStartTime)
          .eq(BeltPerformanceMonth::getEndTime, lastStatisticsEndTime));
      if(ObjectUtil.isNotNull(selectMonthData)){
        log.warn("皮带{}开始时间{}结束时间{}的一月能耗数据已存在，无需重复计算", beltCode, lastStatisticsStartTime, lastStatisticsEndTime);
        return;
      }
      // 负载率、负载负载率、空负载率
      BigDecimal oneMonthAvgAllLoadRate = dayDataListByBelt.stream().map(BeltPerformanceDay::getAvgAllLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      BigDecimal oneMonthAvgLoadRate = dayDataListByBelt.stream().map(BeltPerformanceDay::getAvgLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      BigDecimal oneMonthAvgNoLoadRate = dayDataListByBelt.stream().map(BeltPerformanceDay::getAvgNoLoadRate).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 0, RoundingMode.HALF_UP);
      // 负载时长、空载时长
      BigDecimal oneMonthLoadRunningTime = dayDataListByBelt.stream().map(BeltPerformanceDay::getLoadRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneMonthUnLoadRunningTime = dayDataListByBelt.stream().map(BeltPerformanceDay::getNoLoadRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneMonthAllRunningTime = dayDataListByBelt.stream().map(BeltPerformanceDay::getAllRuntime).reduce(BigDecimal.ZERO, BigDecimal::add);
      // 负载率占比
      BigDecimal oneMonthLoadRateProportion = oneMonthLoadRunningTime.divide(oneMonthAllRunningTime, 2, RoundingMode.HALF_UP);
      // 所有电耗、负载电耗、空载电耗
      BigDecimal oneMonthAllPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getTotalPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneMonthLoadPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getLoadPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal oneMonthUnLoadPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getNoLoadPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      // 运行效率
      BigDecimal oneMonthRunningEfficiency = dayDataListByBelt.stream().map(BeltPerformanceDay::getOperationEfficiency).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(dayDataListByBelt.size()), 2, RoundingMode.HALF_UP);
      // 运输量
      BigDecimal oneMonthTransport = dayDataListByBelt.stream().map(BeltPerformanceDay::getTotalTransport).reduce(BigDecimal.ZERO, BigDecimal::add);
      //构建并保存数据
      BeltPerformanceMonth beltPerformanceMonth = new BeltPerformanceMonth();
      beltPerformanceMonth.setStartTime(lastStatisticsStartTime);
      beltPerformanceMonth.setEndTime(lastStatisticsEndTime);
      beltPerformanceMonth.setBeltId(dayDataListByBelt.get(0).getBeltId());
      beltPerformanceMonth.setBeltCode(beltCode);
      beltPerformanceMonth.setAvgAllLoadRate(oneMonthAvgAllLoadRate);
      beltPerformanceMonth.setAvgLoadRate(oneMonthAvgLoadRate);
      beltPerformanceMonth.setAvgNoLoadRate(oneMonthAvgNoLoadRate);
      beltPerformanceMonth.setLoadRuntime(oneMonthLoadRunningTime);
      beltPerformanceMonth.setNoLoadRuntime(oneMonthUnLoadRunningTime);
      beltPerformanceMonth.setAllRuntime(oneMonthAllRunningTime);
      beltPerformanceMonth.setLoadRateRatio(oneMonthLoadRateProportion);
      beltPerformanceMonth.setTotalPowerConsumption(oneMonthAllPowerConsumption);
      beltPerformanceMonth.setLoadPowerConsumption(oneMonthLoadPowerConsumption);
      beltPerformanceMonth.setNoLoadPowerConsumption(oneMonthUnLoadPowerConsumption);
      beltPerformanceMonth.setOperationEfficiency(oneMonthRunningEfficiency);
      beltPerformanceMonth.setTotalTransport(oneMonthTransport);
      // 峰谷平尖电耗
      BigDecimal peakPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getPeakPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal valleyPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getValleyPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal flatPowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getFlatPowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      BigDecimal spikePowerConsumption = dayDataListByBelt.stream().map(BeltPerformanceDay::getSpikePowerConsumption).reduce(BigDecimal.ZERO, BigDecimal::add);
      beltPerformanceMonth.setPeakPowerConsumption(peakPowerConsumption);
      beltPerformanceMonth.setValleyPowerConsumption(valleyPowerConsumption);
      beltPerformanceMonth.setFlatPowerConsumption(flatPowerConsumption);
      beltPerformanceMonth.setSpikePowerConsumption(spikePowerConsumption);
      beltPerformanceMonth.setWorkRate(null);
      beltPerformanceMonth.setWorkEfficiency(oneMonthTransport.divide(beltPerformanceMonth.getAllRuntime().divide(BigDecimal.valueOf(60)), 2, RoundingMode.HALF_UP));
      beltPerformanceMonthService.save(beltPerformanceMonth);
    });
  }

  /**
   * 构建皮带信息输入参数DTO
   * @param basicDeviceRespVOList 基础设备响应VO列表
   * @param data 缓存数据
   * @return
   */
  private List<BeltInfoInputParamDTO> buildBeltInfoInputParamDTOByBasicDeviceRespVO(List<BasicDeviceRespVO> basicDeviceRespVOList, BeltRouteBelongData data){

    List<BeltInfoInputParamDTO> resultList = new ArrayList<>();
    for (BasicDeviceRespVO basicDeviceRespVO : basicDeviceRespVOList){
      BeltInfoInputParamDTO beltDTO = new BeltInfoInputParamDTO();
      beltDTO.setBeltCode(basicDeviceRespVO.getDeviceCode());
      beltDTO.setBeltSpeed(basicDeviceRespVO.getDeviceSpeed());
      beltDTO.setBeltLength(basicDeviceRespVO.getDeviceLength());
      beltDTO.setBeltWidth(basicDeviceRespVO.getDeviceWidth());
      beltDTO.setMotorPoles(basicDeviceRespVO.getMotorPoles());
      beltDTO.setMotorLossFactor(basicDeviceRespVO.getMotorLossFactor());
      beltDTO.setMotorRatedPower(basicDeviceRespVO.getMotorRatedPower());
      beltDTO.setMotorRatedEfficiency(basicDeviceRespVO.getMotorRatedEfficiency());
      beltDTO.setMotorImbalanceNum(basicDeviceRespVO.getMotorImbalanceNum());
      beltDTO.setFlowNo(data.getFlowNo());
      beltDTO.setTaskId(data.getTaskId());
      beltDTO.setTaskStartTime(data.getStartTime());
      beltDTO.setTaskEndTime(data.getEndTime());
      resultList.add(beltDTO);
    }
    return resultList;
  }

  /**
   * 根据时间获取当前电的类型
   * @param dateTime
   * @return
   */
  private Integer getPowerTypeByTime(LocalDateTime dateTime){
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
    String time = dateTime.format(formatter);
    BasicPowerType type = basicPowerTypeService.getOne(new LambdaQueryWrapper<BasicPowerType>()
        .ge(BasicPowerType::getEndTime, time)
        .le(BasicPowerType::getStartTime, time)
        .last("limit 1"));
    return type.getType();
  }

  /**
   * 根据时间段获取皮带总运输量 和 负载信号
   * @param flowNo 路线唯一标志
   * @param beltCode 皮带编号
   * @param startTime 分钟开始时间
   * @param endTime 分钟结束时间
   */
  private Map<String, Object> getBeltTotalTransportByTimeSlotAndLoadSignal(int flowNo ,String beltCode ,LocalDateTime startTime, LocalDateTime endTime){

    List<BeltTrack> beltTrackList = beltTrackService.list(new LambdaQueryWrapper<BeltTrack>()
        .eq(BeltTrack::getFlowNo, flowNo)
        .eq(BeltTrack::getBeltCode, beltCode)
        .between(BeltTrack::getRecordTime, startTime, endTime)
        .orderByAsc(BeltTrack::getRecordTime));
    Map<String,Object> resultMap = new HashMap<>();

    if(CollectionUtils.isEmpty(beltTrackList)){
      resultMap.put("totalTransport", BigDecimal.ZERO);
      resultMap.put("loadSignal", 0);
      return resultMap;
    }
    BigDecimal totalTransport = beltTrackList.get(beltTrackList.size() - 1).getAddUpWeight().subtract(beltTrackList.get(0).getAddUpWeight());
    resultMap.put("totalTransport", totalTransport);
    int loadStatusSize = beltTrackList.stream().filter(beltTrack -> beltTrack.getLoadStatus() == 1).collect(Collectors.toList()).size();
    if(loadStatusSize > 0){
      resultMap.put("loadSignal", 1);
    }else {
      resultMap.put("loadSignal", 0);
    }
    return resultMap;
  }


  /**
   * 根据皮带轨迹数据获取皮带任务开始时间
   * @param beltTrackList
   * @return
   */
  private LocalDateTime getRouteTaskStartTimeByBeltTrack(List<BeltTrack> beltTrackList){
    List<BeltTrack> beltTrackListRunning = beltTrackList.stream().filter(e -> e.getStartFlag() == 1).sorted(
        Comparator.comparing(BeltTrack::getRecordTime)).collect(Collectors.toList());

    if(CollectionUtils.isEmpty(beltTrackListRunning)){
      return null;
    }
    return beltTrackListRunning.get(0).getRecordTime();
  }

  /**
   * 根据皮带轨迹数据获取皮带任务结束时间
   * @param beltTrackList
   * @param flowNo
   * @param taskId
   * @return
   */
  private LocalDateTime getRouteTaskEndTimeByBeltTrack(List<BeltTrack> beltTrackList, Short flowNo, Long taskId){
    List<BeltTrack> beltTrackListRunning = beltTrackList.stream().filter(e -> e.getStartFlag() == 1).sorted(
        Comparator.comparing(BeltTrack::getRecordTime)).collect(Collectors.toList());
    List<BeltTrack> beltTrackListStopping = beltTrackList.stream().filter(e -> e.getStartFlag() == 0).sorted(
        Comparator.comparing(BeltTrack::getRecordTime)).collect(Collectors.toList());

    if(CollectionUtils.isEmpty(beltTrackListRunning)){
      return null;
    }

    //获取flowNo最后的一条数据，若是是taskId不一致，则认为任务已经完成
    BeltTrack lastBeltTrack = beltTrackService.getOne(new QueryWrapper<BeltTrack>().eq("flow_no", flowNo).orderByDesc("record_time").last("limit 1"));
    if(!lastBeltTrack.getTaskId().equals(taskId)){
      return beltTrackList.get(beltTrackList.size()-1).getRecordTime();
    }

    if(CollectionUtils.isEmpty(beltTrackListStopping)){
      return null;
    }

    LocalDateTime startTime = beltTrackListRunning.get(0).getRecordTime();
    LocalDateTime endTime = beltTrackListStopping.get(beltTrackListStopping.size()-1).getRecordTime();
    if(startTime.isBefore(endTime)){
      return endTime;
    }else {
      //代表还在执行，还没有停止
      return null;
    }
  }
}
